#include "chartreader.h"
#include "vdsdevice.h"
#include <QThread>
#include <QtMath>
#include "measure.h"
#include "measure_p.h"
#include "measure_dataParse.h"
#include "vdsadapter.h"
#include "log4z.h"
#include "logger.h"

#define MINIMUM_WAVE_SIZE  21
#define PACKET_BUFFER_SIZE 1024 * 32
#define TRIGGLE_VOLT_RATIO 0.8
#define TROW_HEAD_DATA_COUNT 5

struct VdsParameterPrivate
{
    QString HORI_SCAL; // 时基档位
    QString HORI_OFFS; // 时基偏移

    QString CH1_SCAL; // 电压档位
    QString CH1_OFFS; // 电压偏移

    QString CH2_SCAL; // 电流档位
    QString CH2_OFFS; // 电流偏移

    QString TRIG_SING_EDGE_SOUR; // 触发通道
    QString TRIG_SING_EDGE_SWE; // 触发方式
    QString TRIG_SING_EDGE_LEV; // 触发电平
};

class ChartReaderPrivate
{
public:
    unsigned char usbtmc_packet_buff[PACKET_BUFFER_SIZE];
    int m_packetBufferOffset;
    int m_packetBufferLength;
    int m_curThrowCount;

    VdsParameterPrivate m_parameter;
    VdsParameterPtr m_settings;
    VdsDevice m_vds;

    int read();
    double round(double value, int decimal = 1);
    double calculate(double value);
    void logParameter();
};

double ChartReaderPrivate::round(double value, int decimal)
{
    double base = qPow(10, decimal);
    return qRound(value * base) / base;
}

double ChartReaderPrivate::calculate(double value)
{
    return qMax(0.0, round(value));
}

ChartReader::ChartReader()
{
    d = new ChartReaderPrivate;
}

ChartReader::~ChartReader()
{
    delete d;
}

void ChartReaderPrivate::logParameter()
{
    QString inputParameter = QString("testVolt: %1, pulseWidth: %2, pulseInterval: %3").arg(m_settings->testVolt).arg(m_settings->pulseWidth).arg(m_settings->pulseInterval);
    QString probeRateParameter = QString("probeRate_ch1: %1, probeRate_ch2: %2").arg(m_settings->probeRate[VoltageChannel]).arg(m_settings->probeRate[CurrentChannel]);
    QString channelCoefParameter = QString("channelCoef_ch1: %1, channelCoef_ch2: %2").arg(m_settings->channelCoef[VoltageChannel]).arg(m_settings->channelCoef[CurrentChannel]);
    QString autoParameter = QString("HORI_SCAL: %1, HORI_OFFS: %2\nCH1_SCAL: %3, CH1_OFFS: %4\nCH2_SCAL: %5, CH2_OFFS: %6\nTRIG_SING_EDGE_LEV: %7")
            .arg(m_parameter.HORI_SCAL).arg(m_parameter.HORI_OFFS).arg(m_parameter.CH1_SCAL).arg(m_parameter.CH1_OFFS).arg(m_parameter.CH2_SCAL).arg(m_parameter.CH2_OFFS).arg(m_parameter.TRIG_SING_EDGE_LEV);
    QString parameterString = QString("%1\n%2, %3\n%4\n").arg(inputParameter).arg(probeRateParameter).arg(channelCoefParameter).arg(autoParameter);
    sysDebug("start(): %s", parameterString.toLatin1().data());
}

void ChartReader::onSetParameter(const VdsParameterPtr parameter)
{
    VdsAdapter vdsAdaper;
    d->m_parameter.HORI_SCAL = vdsAdaper.timebaseScale(parameter->pulseWidth);
    d->m_parameter.HORI_OFFS = vdsAdaper.timebaseOffset();
    d->m_parameter.CH1_SCAL = vdsAdaper.voltageScale(parameter->testVolt, parameter->probeRate[VoltageChannel]);
    d->m_parameter.CH1_OFFS = vdsAdaper.voltageOffset();
    d->m_parameter.CH2_SCAL = vdsAdaper.voltageScale(parameter->testVolt, parameter->probeRate[CurrentChannel]);
    d->m_parameter.CH2_OFFS = vdsAdaper.voltageOffset();
    d->m_parameter.TRIG_SING_EDGE_SOUR = "CH1";
    d->m_parameter.TRIG_SING_EDGE_SWE = "NORM";
    d->m_parameter.TRIG_SING_EDGE_LEV = vdsAdaper.triggleLevel(parameter->testVolt * TRIGGLE_VOLT_RATIO, parameter->probeRate[VoltageChannel], d->m_parameter.CH1_SCAL);
    d->m_settings = parameter;
    d->logParameter();
}

bool ChartReader::open()
{
    bool opened = d->m_vds.open();
    if (!opened)
    {
        sysError("%s: vds open fail: %s", Q_FUNC_INFO, strerror(errno));
        emit measureError(OpenFail);
        return false;
    }

    QString cmd = QString(":HORI:SCAL %1;:HORI:OFFS %2;:CH1:DISP ON;:CH1:COUP DC;:CH1:INVErse OFF;:CH1:SCAL %3;:CH1:OFFS %4;:ACQ:DEPMEM 1K;:ACQ:PREC 8;:TRIG:TYPE SING;:TRIG:SING:MODE EDGE;:TRIG:SING:SWE %5;:TRIG:SING:HOLD 0.001;:TRIG:SING:EDGE:SOUR %6;:TRIG:SING:EDGE:COUP DC;:TRIG:SING:EDGE:SLOP RISE;:TRIG:SING:EDGE:LEV %7;:MEAS:DISP OFF;:CH2:SCAL %8;:CH2:OFFS %9;:CH2:DISP ON;:CH2:COUP DC;:CH2:INVErse OFF;")
            .arg(d->m_parameter.HORI_SCAL)
            .arg(d->m_parameter.HORI_OFFS)
            .arg(d->m_parameter.CH1_SCAL)
            .arg(d->m_parameter.CH1_OFFS)
            .arg(d->m_parameter.TRIG_SING_EDGE_SWE)
            .arg(d->m_parameter.TRIG_SING_EDGE_SOUR)
            .arg(d->m_parameter.TRIG_SING_EDGE_LEV)
            .arg(d->m_parameter.CH2_SCAL)
            .arg(d->m_parameter.CH2_OFFS);

    QByteArray ba = cmd.toUtf8();
    const char * settingStr = ba.data();

    if (!d->m_vds.write(settingStr, strlen(settingStr)))
    {
       sysError("%s: vds write fail: %s", Q_FUNC_INFO, strerror(errno));
       emit measureError(InitFail);
       return false;
    }

    emit measureError(OpenSuccess);
    d->m_curThrowCount = 0;
    return true;
}

bool ChartReader::close()
{
   return d->m_vds.close();
}

int ChartReaderPrivate::read()
{
    int retCount = 0;
    int receivedTotal = 0;
    int num = 0;
    unsigned char tp = 0;

    int length = 0;
    int dataLen = 0;
    unsigned char *dataSrc = Q_NULLPTR;
    const char *cmd_str = ":WAV:DATA?\r\n";

    if (!m_vds.write(cmd_str, strlen(cmd_str) + 1))
    {
        sysError("%s: vds.write() fail: %s", Q_FUNC_INFO, strerror(errno));
        return -1;
    }

    while (true)
    {
        retCount = m_vds.read(usbtmc_packet_buff, PACKET_BUFFER_SIZE);

        if (usbtmc_packet_buff[0] == '#' && receivedTotal == 0)
        {
            num = usbtmc_packet_buff[1] - '0';
            tp = usbtmc_packet_buff[num + 2];
            usbtmc_packet_buff[num + 2] = 0;
            length = atoi((const char *)usbtmc_packet_buff + 2);
            usbtmc_packet_buff[num + 2] = tp;
            dataSrc = &usbtmc_packet_buff[num + 2];
            dataLen = retCount - (num + 2);

            m_packetBufferOffset = num + 2;
            m_packetBufferLength = dataLen;
        }
        else
        {
            dataSrc = usbtmc_packet_buff;
            dataLen = retCount;

            m_packetBufferOffset = 0;
            m_packetBufferLength = dataLen;
        }

        receivedTotal += dataLen;

        if (receivedTotal >= length)
        {
            break;
        }

        QThread::usleep(1000);
    }

    return dataLen;
}

void ChartReader::measure()
{
    // 1.读取
    if (d->read() < 0)
    {
        sysError("vds6000Read ret: FALSE");
        emit measureError(ReadFail);
        return;
    }

    // 2.计算
    MeasureResult result;
    BOOL ret = vds6000Measure(&d->usbtmc_packet_buff[d->m_packetBufferOffset], d->m_packetBufferLength, &result);
    if (ret == FALSE)
    {
        sysError("vds6000Measure ret: FALSE");
        if (CHECKEMPTY_VALUE == protocolData.chNums)
        {
            emit measureError(DataEmpty);
            return;
        }

        emit measureError(MeasureFail);
        return;
    }

    if (result.measItem[VoltageChannel].overflow)
    {
        sysError("vds6000Measure: voltage dataOverflow");
        emit measureError(DataOverflowCh1);
    }
    else if (result.measItem[CurrentChannel].overflow)
    {
        sysError("vds6000Measure: current dataOverflow");
        emit measureError(DataOverflowCh2);
    }

    // 丢弃开始检测的几帧波形
    if (d->m_curThrowCount < TROW_HEAD_DATA_COUNT)
    {
        d->m_curThrowCount++;
        sysDebug("vds6000Measure: throw data times: %d", d->m_curThrowCount);
        return;
    }

    // 3.转换
    VdsMeasureDataPtr measureData = VdsMeasureDataPtr(new VdsMeasureData[DataChannelNum]);    
    const double ratio[DataChannelNum] = { // 通道转换系数
        d->m_settings->probeRate[VoltageChannel] * 0.001 / d->m_settings->channelCoef[VoltageChannel],
        d->m_settings->probeRate[CurrentChannel] * 0.001 / d->m_settings->channelCoef[CurrentChannel]};

    for (int ch = 0; ch < DataChannelNum; ++ch)
    {
        VdsMeasureData *data = measureData.data() + ch;
        data->vmax = d->calculate(ratio[ch] * result.measItem[ch].max      + d->m_settings->zeroPos[ch]);
        data->vmin = d->calculate(ratio[ch] * result.measItem[ch].min      + d->m_settings->zeroPos[ch]);
        data->vtop = d->calculate(ratio[ch] * result.measItem[ch].Vtop     + d->m_settings->zeroPos[ch]);
        data->vrms = d->calculate(ratio[ch] * result.measItem[ch].pauseRMS + d->m_settings->zeroPos[ch]);
        data->vamp = d->calculate(ratio[ch] * result.measItem[ch].Vamp);
        data->pwid = d->calculate(result.measItem[ch].PWidth);
        data->overflow = result.measItem[ch].overflow;

        data->waveSize = measureParam.measItem[ch].waveSize;
        if (measureData->waveSize <= MINIMUM_WAVE_SIZE)
        {
            sysDebug("vds6000Measure: waveSize <= %d, discard this data.", MINIMUM_WAVE_SIZE);
            return;
        }

        for (int i = 0; i < data->waveSize; ++i)
        {
            data->waveData[i] = d->m_settings->zeroPos[ch] +
                    measureParam.measItem[ch].adc_without_zero[i] * measureParam.measItem[ch].voltUnit * ratio[ch];
        }
    }

    emit dataReady(measureData);
}
